/*------------------------------------------------------------------------------
* Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team
*
* Distributable under the terms of either the Apache License (Version 2.0) or
* the GNU Lesser General Public License, as specified in the LICENSE file.
------------------------------------------------------------------------------*/
#include "../Lucene.hpp"
#include "PrefixQuery.hpp"
#include "BooleanQuery.hpp"
//#include "BooleanClause.hpp"
#include "../index/Term.hpp"
#include "../index/Terms.hpp"
//#include "../index/IndexReader.hpp"
#include "../search/TermQuery.hpp"
#include "../util/StringBuffer.hpp"
//#include "../util/BitSet.hpp"
#include "../../common/Containers.hpp" // For CollatorStrncmp
#include "../../searcher/Searcher.hpp"
#include "unicode/ustring.h"

U_STRING_DECL(cStrClassName, "PrefixQuery", 11);
CL_NS_DEF(search)
void PrefixQuery::InitStringDecl() {
	U_STRING_INIT(cStrClassName, "PrefixQuery", 11);
}
CL_NS_END

CL_NS_USE(index)
CL_NS_DEF(search)
using namespace Atoll;
using namespace Common;

PrefixQuery::PrefixQuery(Term* Prefix)
{
	//Func - Constructor.
	//       Constructs a query for terms starting with prefix
	//Pre  - Prefix != NULL
	//Post - The instance has been created

	//Get a pointer to Prefix
	prefix = _CL_POINTER(Prefix);
}

PrefixQuery::PrefixQuery(const PrefixQuery& clone): Query(clone)
{
	prefix = _CL_POINTER(clone.prefix);
}
Query* PrefixQuery::clone() const
{
	return _CLNEW PrefixQuery(*this);
}

Term* PrefixQuery::getPrefix(bool pointer)
{
	if (pointer)
		return _CL_POINTER(prefix);
	else
		return prefix;
}

PrefixQuery::~PrefixQuery()
{
	//Func - Destructor
	//Pre  - true
	//Post - The instance has been destroyed.

	//Delete prefix by finalizing it
	_CLDECDELETE(prefix);
}


//! Returns a hash code value for this object
size_t PrefixQuery::hashCode() const
{
	return 0; // Similarity::floatToByte(getBoost()) ^ prefix->hashCode();
}

const UChar *PrefixQuery::getQueryName()const
{
	//Func - Returns the name "PrefixQuery"
	//Pre  - true
	//Post - The string "PrefixQuery" has been returned

	return getClassName();
}
const UChar *PrefixQuery::getClassName()
{
	//Func - Returns the name "PrefixQuery"
	//Pre  - true
	//Post - The string "PrefixQuery" has been returned

	return cStrClassName;
}

bool PrefixQuery::equals(Query * other) const
{
	if (!(other->instanceOf(PrefixQuery::getClassName())))
		return false;

	PrefixQuery* rq = static_cast<PrefixQuery *>(other);
	bool ret = (this->getBoost() == rq->getBoost()) && (this->prefix->equals(rq->prefix));

	return ret;
}

Query* PrefixQuery::rewrite(Searcher *inSearcher)
{
	BooleanQuery* query = _CLNEW BooleanQuery();
	TermEnum* enumerator = inSearcher->GetTermEnum(prefix);
	Term* lastTerm = NULL;
	try {
		const UChar *prefixText = prefix->text();
		const UChar *prefixField = prefix->field();
		const UChar *tmp;
		size_t i;
		int32_t prefixLen = prefix->textLength();
		do {
			lastTerm = enumerator->term(false);
			if (lastTerm != NULL && _tcscmp(lastTerm->field(), prefixField) == 0) {
				// See if term->text() starts with prefixText
				int32_t termLen = lastTerm->textLength();
				if (prefixLen > termLen)
					break; //the prefix is longer than the term, can't be matched
				// Check for prefix match in reverse, since most change will be at the end
				tmp = lastTerm->text();
				for (i = prefixLen - 1; i != (size_t)-1; --i) {
					if (tmp[i] != prefixText[i]) {
						// -DC- Add a collator comparison
						if (Common::CollatorStrncmp(prefixText, tmp, prefixLen) != 0)
							tmp = NULL; //signals inequality
						break;
					}
				}
				if (tmp == NULL)
					break;
				TermQuery* tq = _CLNEW TermQuery(lastTerm);	  // found a match
				tq->setBoost(getBoost());                // set the boost
				query->add(tq, true, false, false);		  // add to query
			}
			else
				break;
			//_CLDECDELETE(lastTerm);
		}
		while (enumerator->next());
	}
	_CLFINALLY(
	  enumerator->close();
	  _CLDELETE(enumerator);
	  //_CLDECDELETE(lastTerm);
	);
	//_CLDECDELETE(lastTerm);


	// Optimize 1-clause queries
	size_t size = query->getClauseCount();
	if (size == 1) {
		BooleanClause* c = 0;
		query->getClauses(&c);
		// Just return clause
		if (!c->prohibited) {
			c->deleteQuery = false;
			Query* ret = c->query;
			_CLDELETE(query);
			return ret;
		}
	}

	return query;
}

Query* PrefixQuery::combine(Query** queries)
{
	return Query::mergeBooleanQueries(queries);
}

UChar *PrefixQuery::toString(const UChar *field) const
{
	//Func - Creates a user-readable version of this query and returns it as as string
	//Pre  - field != NULL
	//Post - a user-readable version of this query has been returned as as string

	//Instantiate a stringbuffer buffer to store the readable version temporarily
	CL_NS(util)::StringBuffer buffer;
	//check if field equal to the field of prefix
	if (field == NULL || _tcscmp(prefix->field(), field) != 0) {
		//Append the field of prefix to the buffer
		buffer.append(prefix->field());
		//Append a colon
		buffer.append(_T(":"));
	}
	//Append the text of the prefix
	buffer.append(prefix->text());
	//Append a wildchar character
	buffer.append(_T("*"));
	//if the boost factor is not eaqual to 1
	if (getBoost() != 1.0f) {
		//Append ^
		buffer.append(_T("^"));
		//Append the boost factor
		buffer.appendFloat( getBoost(), 1);
	}
	//Convert StringBuffer buffer to UChar block and return it
	return buffer.toString();
}

Atoll::EntrySet* PrefixQuery::Search(Searcher *inSearcher)
{
	// Invalid, as PrefixQuery is rewriten as a BooleanQuery
	DEF_Exception(AppException::InvalidOperation, "PrefixQuery::Search");

	return NULL;
}

/*
PrefixFilter::PrefixFilter(Term* prefix)
{
	this->prefix = _CL_POINTER(prefix);
}

PrefixFilter::~PrefixFilter()
{
	_CLDECDELETE(prefix);
}

PrefixFilter::PrefixFilter( const PrefixFilter& copy) :
		prefix(_CL_POINTER(copy.prefix))
{
}

Filter* PrefixFilter::clone() const
{
	return _CLNEW PrefixFilter(*this);
}

UChar *PrefixFilter::toString()
{
	//Instantiate a stringbuffer buffer to store the readable version temporarily
	CL_NS(util)::StringBuffer buffer;
	//check if field equal to the field of prefix
	if (prefix->field() != NULL) {
		//Append the field of prefix to the buffer
		buffer.append(prefix->field());
		//Append a colon
		buffer.append(_T(":"));
	}
	//Append the text of the prefix
	buffer.append(prefix->text());
	buffer.append(_T("*"));

	//Convert StringBuffer buffer to UChar block and return it
	return buffer.toString();
}

// Returns a BitSet with true for documents which should be permitted in
// search results, and false for those that should not.
BitSet* PrefixFilter::bits(IndexReader* reader)
{
	BitSet* bts = _CLNEW BitSet( reader->maxDoc());
	TermEnum* enumerator = reader->terms(prefix);
	TermDocs* docs = reader->termDocs();
	const UChar *prefixText = prefix->text();
	const UChar *prefixField = prefix->field();
	const UChar *tmp;
	size_t i;
	int32_t prefixLen = prefix->textLength();
	Term* lastTerm = NULL;

	try {
		do {
			lastTerm = enumerator->term(false);
			if (lastTerm != NULL && _tcscmp(lastTerm->field(), prefixField) == 0) {
				//now see if term->text() starts with prefixText
				int32_t termLen = lastTerm->textLength();
				if (prefixLen > termLen)
					break; //the prefix is longer than the term, can't be matched

				//check for prefix match in reverse, since most change will be at the end
				tmp = lastTerm->text();
				for (i = prefixLen - 1;i != -1; --i) {
					if (tmp[i] != prefixText[i]) {
						tmp = NULL; //signals inequality
						break;
					}
				}
				if (tmp == NULL)
					break;

				docs->seek(enumerator);
				while (docs->next()) {
					bts->set(docs->doc());
				}
			}
		}
		while (enumerator->next());
	}
	_CLFINALLY(
	  docs->close();
	  _CLDELETE(docs);
	  enumerator->close();
	  _CLDELETE(enumerator);
	)

	return bts;
}
*/

CL_NS_END
